#include "record_recorder.h"
#include <sstream>
#include <chrono>
#include <iomanip>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/statvfs.h>
#include <unistd.h>
#include <libgen.h>
#include <errno.h>
#include <string.h>
#include "utils_string_ex.h"
#include "common_config_manager.h"
#include "media_snap.h"
#include "record_index_writer.h"
#include "common_event.h"
#include "utils_log.h"
#include "utils_file_ex.h"
#include "record_utils.h"
#include "utils_time_cost.h"

namespace El {
namespace Record {

Recorder::Recorder(int32_t ch) : ch_(ch)
{
    // 注册报警事件监听
    auto &event = Common::Event::GetInstance();
    eventHandle_ = event.Subscribe("alarm", [this](const std::string &eventName, const nlohmann::json &data) {
        (void)eventName;
        if (data["channel"] == ch_) {
            EL_INFO("Received alarm event for channel {}", ch_);
            // std::lock_guard<std::mutex> lock(mutex_);
            // if (writer_) {
            //     writer_->AddEvent(data);
            // }
        }
    });

    // 初始化时间检测
    lastCheckTime_ = std::chrono::system_clock::now();
    lastDay_ = std::chrono::system_clock::to_time_t(lastCheckTime_); // 直接使用当前时间作为初始值
}

Recorder::~Recorder()
{
    auto &event = Common::Event::GetInstance();
    event.Unsubscribe("alarm", eventHandle_);

    Stop();
}

bool Recorder::SetConfig(uint32_t maxFileDuration, uint64_t maxFileSize, std::string storePath, bool overwrite)
{
    maxFileDuration_ = maxFileDuration;
    // MB转换为B
    maxFileSize_ = maxFileSize * 1024 * 1024;
    storePath_ = storePath;
    overwrite_ = overwrite;
    PathUtils::GetInstance().SetStorePath(storePath);
    EL_INFO("Set config maxFileDuration {}s maxFileSize {}MB storePath {} overwrite {}", maxFileDuration, maxFileSize,
             storePath, overwrite);
    return true;
}

bool Recorder::Start()
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (running_) {
        EL_WARN("Recorder already running for channel {}", ch_);
        return false;
    }
    running_ = true;

    EL_INFO("Recorder started for channel {}", ch_);
    recordThread_ = std::make_unique<std::thread>(&Recorder::RecordThread, this);
    lastTime_ = std::chrono::steady_clock::now();
    return true;
}

void Recorder::Stop()
{
    {
        std::lock_guard<std::mutex> lock(mutex_);
        if (!running_) {
            EL_WARN("Recorder already stopped for channel {}", ch_);
            return;
        }
        running_ = false;
    }

    if (recordThread_ && recordThread_->joinable()) {
        recordThread_->join();
    }

    // 通知GUI
    nlohmann::json jsonData;
    jsonData["event"] = "record.stop";
    Common::Event::GetCustomInstance("gui").Notify("prompt", jsonData);

    EL_INFO("Recorder stopped for channel {}", ch_);
}

void Recorder::OnEvent(const nlohmann::json &alarm)
{
    std::lock_guard<std::mutex> lock(alarmMutex_);
    auto currentTime = std::chrono::steady_clock::now();
    AlarmInfo info{alarm, currentTime, currentTime, 0};
    newList_.push(info);
}

bool Recorder::StartManual()
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (isManualRecording_) {
        EL_WARN("Manual recording already in progress for channel {}", ch_);
        return false;
    }
    isManualRecording_ = true;

    // 添加手动录像事件
    nlohmann::json manualEvent;
    manualEvent["code"] = "manual";
    manualEvent["channel"] = ch_;
    manualEvent["type"] = "start";

    auto currentTime = std::chrono::steady_clock::now();
    AlarmInfo info{manualEvent, currentTime, currentTime, 0};
    newList_.push(info);

    return true;
}

void Recorder::StopManual()
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (!isManualRecording_) {
        return;
    }
    isManualRecording_ = false;

    // 添加手动录像停止事件
    nlohmann::json manualEvent;
    manualEvent["code"] = "manual";
    manualEvent["channel"] = ch_;
    manualEvent["type"] = "stop";

    auto currentTime = std::chrono::steady_clock::now();
    AlarmInfo info{manualEvent, currentTime, currentTime, 0};
    newList_.push(info);
}

bool Recorder::CheckEvents()
{
    std::lock_guard<std::mutex> lock(mutex_);
    auto currentTime = std::chrono::steady_clock::now();

    while (!newList_.empty()) {
        const auto &item = newList_.front();
        std::string alarmCode = item.alarm["code"].get<std::string>();

        // 处理手动录像停止事件
        if (alarmCode == "manual" && item.alarm["type"] == "stop") {
            auto iter = curMap_.find("manual");
            if (iter != curMap_.end()) {
                curMap_.erase(iter);
            }
            newList_.pop();
            continue;
        }

        auto iter = curMap_.find(alarmCode);
        if (iter != curMap_.end()) {
            /// 更新录像文件
            iter->second.updateTime = currentTime;
        } else {
            if (curMap_.empty()) {
                StopStream();
                StartStream();
                CreateNewFile();
            }
            AlarmInfo info{item.alarm, currentTime, currentTime, 0};
            curMap_[alarmCode] = info;
        }
        newList_.pop();
    }

    // 一秒检测一次
    if (currentTime - lastTime_ >= std::chrono::seconds(1)) {
        lastTime_ = currentTime;
        for (auto iter = curMap_.begin(); iter != curMap_.end();) {
            if (iter->first == "manual") {
                iter++;
                continue;
            }

            /// 报警录像超过录像时长,10s
            /// 报警录像超过最大录像时长,300s
            if ((currentTime - iter->second.updateTime > std::chrono::seconds(10)) ||
                (currentTime - iter->second.createTime > std::chrono::seconds(300))) {
                curMap_.erase(iter++);
            } else {
                iter++;
            }
        }
    }

    bool need = !curMap_.empty();
    if (!need) {
        CloseCurrentFile();
        StopStream();
    }

    return need;
}

bool Recorder::CheckTimeJump()
{
    auto now = std::chrono::system_clock::now();
    auto nowTime = std::chrono::system_clock::to_time_t(now);
    auto lastTime = std::chrono::system_clock::to_time_t(lastCheckTime_);

    // 获取时间信息
    auto lastTm = std::localtime(&lastTime);
    auto nowTm = std::localtime(&nowTime);

    // 检查是否跨天
    bool isDayChanged =
        (lastTm->tm_year != nowTm->tm_year || lastTm->tm_mon != nowTm->tm_mon || lastTm->tm_mday != nowTm->tm_mday);

    // 检查时间差
    int64_t timeDiff = std::abs(nowTime - lastTime);
    bool isTimeJump = (timeDiff > 5); // 超过5秒认为是时间跳变

    // 处理时间变化
    if (isTimeJump || isDayChanged) {
        if (isTimeJump) {
            if (isDayChanged) {
                EL_INFO("Time jump with day change detected on channel {}, from {}-{}-{} to {}-{}-{}, time diff: {}s",
                         ch_, lastTm->tm_year + 1900, lastTm->tm_mon + 1, lastTm->tm_mday, nowTm->tm_year + 1900,
                         nowTm->tm_mon + 1, nowTm->tm_mday, timeDiff);
            } else {
                EL_INFO("Time jump detected on channel {}, time diff: {}s", ch_, timeDiff);
            }
        } else {
            EL_INFO("Normal day change detected on channel {}, from {}-{}-{} to {}-{}-{}", ch_, lastTm->tm_year + 1900,
                     lastTm->tm_mon + 1, lastTm->tm_mday, nowTm->tm_year + 1900, nowTm->tm_mon + 1, nowTm->tm_mday);
        }

        lastCheckTime_ = now;
        lastDay_ = nowTime;
        return true;
    }

    lastCheckTime_ = now;
    lastDay_ = nowTime;
    return false;
}

void Recorder::RecordThread()
{
    EL_INFO("Starting record thread for channel {}", ch_);
    while (running_) {

        Utils::TimeCost time_cost("RecordThread", true);
        // 处理事件
        if (!CheckEvents()) {
            std::this_thread::sleep_for(std::chrono::microseconds(20));
            continue;
        }
        time_cost.Mark("CheckEvents");

        bool needNewFile = false;
        // 检查文件时长
        if (writer_->GetDuration() >= maxFileDuration_) {
            EL_INFO("File duration exceeds maxFileDuration {}s file duration {}s", maxFileDuration_,
                     writer_->GetDuration());
            needNewFile = true;
        }

        // 检查文件大小
        if (writer_->GetFileSize() >= maxFileSize_) {
            EL_INFO("File size exceeds maxFileSize {} file size {}", maxFileSize_, writer_->GetFileSize());
            needNewFile = true;
        }
        time_cost.Mark("CheckFileSize");

        // 检查时间跳变
        if (CheckTimeJump()) {
            EL_INFO("Time jump or day change detected, creating new recording file");
            needNewFile = true;
        }
        time_cost.Mark("CheckTimeJump");

        if (needNewFile) {
            std::lock_guard<std::mutex> lock(mutex_);
            CloseCurrentFile();
            CreateNewFile();
        }
        time_cost.Mark("CloseCurrentFile");

        Media::MediaFramePtr frame = stream_->GetFrame(20);
        if (!frame) {
            continue;
        }
        time_cost.Mark("GetFrame");

        if (!writer_->WriteFrame(frame)) {
            EL_ERROR("Failed to write frame to file for channel {}", ch_);
            std::this_thread::sleep_for(std::chrono::seconds(2));
            continue;
        }
        time_cost.Mark("WriteFrame");
        time_cost.Print(true);
    }
    CloseCurrentFile();
    StopStream();
}

void Recorder::StartStream()
{
    if (stream_) {
        return;
    }
    stream_ = Media::StreamSource::Create(ch_, 0);
    stream_->SetInfo(60, "record media data");
    stream_->Start();
}

void Recorder::StopStream()
{
    if (stream_) {
        stream_->Stop();
        stream_.reset();
    }
}

void Recorder::CreateNewFile()
{
    // 获取视频编码配置
    auto video = Common::ConfigManager::GetInstance().GetConfig("/video_encode/" + std::to_string(ch_) + "/" +
                                                                std::to_string(0));
    if (video.is_null()) {
        EL_ERROR("Failed to get video config");
        return;
    }

    uint32_t width = video.value("width", 0u);
    uint32_t height = video.value("height", 0u);
    uint32_t fps = video.value("fps", 0u);
    writer_ = std::make_unique<Muxer::MP4Writer>();
    writer_->SetVideoParams(width, height, fps);

    // 获取音频编码配置
    auto audioConfig = Common::ConfigManager::GetInstance().GetConfig("/audio_encode/" + std::to_string(ch_));
    if (audioConfig.is_null()) {
        EL_ERROR("Failed to get audio config for ch:{}", ch_);
        return;
    }
    int32_t sampleRate = audioConfig.value("sampleRate", 0);
    int32_t channels = audioConfig.value("channel", 0);
    int32_t depth = audioConfig.value("bitRate", 0);
    std::string codec = audioConfig.value("codec", "aac");
    writer_->SetAudioParams(sampleRate, channels, depth, codec);

    std::string filename = GenerateFilename();
    if (!writer_->Open(filename)) {
        EL_ERROR("Failed to open recording file: {}", filename);
        writer_.reset();
        return;
    }

    // 初始化当前文件的索引信息
    char *baseFilename = basename(const_cast<char *>(filename.c_str()));
    currentFileIndex_ = {{"filename", std::string(baseFilename)},
                         {"start_time", std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())},
                         {"channel", ch_},
                         {"stream_type", 0},
                         {"file_size", 0},
                         {"events", nlohmann::json::array()}};
    std::string absolutePath =
        PathUtils::GetInstance().GenerateIndexPath(currentFileIndex_["start_time"].get<int64_t>());
    IndexWriter::GetInstance().WriteVideoIndex(absolutePath, currentFileIndex_);

    // 在创建新录像文件时抓取缩略图
    if (!CaptureAndSaveThumbnail(filename)) {
        EL_WARN("Failed to capture thumbnail for recording: {}", filename);
    }

    // 初始化时间检测
    lastCheckTime_ = std::chrono::system_clock::now();
    lastDay_ = std::chrono::system_clock::to_time_t(lastCheckTime_);

    EL_INFO("Created new recording file: {} for channel {}", filename, ch_);
}

void Recorder::CloseCurrentFile()
{
    if (writer_) {
        // Update index information
        int32_t startTime = currentFileIndex_["start_time"].get<int32_t>();
        currentFileIndex_["end_time"] = startTime + writer_->GetDuration();
        currentFileIndex_["file_size"] = writer_->GetFileSize();
        currentFileIndex_["duration"] = writer_->GetDuration();

        // Write to index file using shared IndexWriter with absolute path
        std::string absolutePath =
            PathUtils::GetInstance().GenerateIndexPath(currentFileIndex_["start_time"].get<int64_t>());
        IndexWriter::GetInstance().WriteVideoIndex(absolutePath, currentFileIndex_);
        currentFileIndex_.clear();

        writer_->Close();
        writer_.reset();
        EL_INFO("Closed current recording file for channel {}", ch_);

        // 同步文件
        sync();
        EL_DEBUG("Synced current recording file for channel {}", ch_);
    }
}

bool Recorder::CaptureAndSaveThumbnail(const std::string &videoFilename)
{
    std::atomic<bool> snapCompleted{false};
    std::mutex mtx;
    std::condition_variable cv;
    Media::MediaFramePtr thumbFrame;

    // 获取小图抓图实例
    auto &snapThumb = Media::ISnap::GetInstance(ch_, MEDIA_VIDEO_ENC_STREAM_SMALL_SNAPSHOT);

    // 注册抓图回调
    int32_t handle = snapThumb.Register([&](const Media::MediaFramePtr &frame) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            thumbFrame = frame;
            snapCompleted = true;
        }
        cv.notify_one();
    });

    if (handle < 0) {
        EL_ERROR("Failed to register thumbnail snap callback");
        return false;
    }

    // 触发抓图
    if (!snapThumb.ManualSnap()) {
        EL_ERROR("Failed to trigger thumbnail snapshot");
        snapThumb.Unregister(handle);
        return false;
    }

    // 等待抓图完成
    {
        std::unique_lock<std::mutex> lock(mtx);
        if (!cv.wait_for(lock, std::chrono::seconds(5), [&]() { return snapCompleted.load(); })) {
            EL_ERROR("Timeout waiting for thumbnail snapshot");
            snapThumb.Unregister(handle);
            return false;
        }
    }

    // 保存缩略图
    if (!thumbFrame || thumbFrame->GetLength() == 0) {
        EL_ERROR("Invalid thumbnail frame");
        snapThumb.Unregister(handle);
        return false;
    }

    // 生成缩略图路径并保存
    std::string thumbnailPath = PathUtils::GetInstance().GenerateThumbnailPath(videoFilename);

    // 创建缩略图目录
    std::string dirPath = thumbnailPath;
    size_t pos = dirPath.find_last_of('/');
    if (pos != std::string::npos) {
        dirPath = dirPath.substr(0, pos);
        if (!Utils::CreateDirectories(dirPath)) {
            EL_ERROR("Failed to create directory: {}", dirPath);
            snapThumb.Unregister(handle);
            return false;
        }
    }

    std::ofstream file(thumbnailPath, std::ios::binary);
    if (!file.is_open()) {
        EL_ERROR("Failed to open thumbnail file: {}", thumbnailPath);
        snapThumb.Unregister(handle);
        return false;
    }

    file.write(reinterpret_cast<const char *>(thumbFrame->GetBuffer()), thumbFrame->GetLength());
    file.close();

    // 清理资源
    snapThumb.Unregister(handle);
    EL_INFO("Saved thumbnail to: {}", thumbnailPath);
    return true;
}

std::string Recorder::GenerateFilename() const
{
    auto now = std::chrono::system_clock::now();
    time_t timestamp = std::chrono::system_clock::to_time_t(now);
    return PathUtils::GetInstance().GenerateFilename(timestamp, ch_, 0, "videos", ".mp4");
}

bool Recorder::GetRecordStatus() const
{
    return writer_ != nullptr;
}

} // namespace Record
} // namespace El